import type { MaybeRef, Ref, WatchSource } from "vue";

import type Fetch from "./Fetch";
import type { CachedData } from "./utils/cache";

export type Service<TData, TParams extends any[]> = (...args: TParams) => Promise<TData>;
export type Subscribe = () => void;

// for Fetch
export interface FetchState<TData, TParams extends any[]> {
  loading: boolean;
  params?: TParams;
  data?: TData;
  error?: Error;
}

export interface PluginReturn<TData, TParams extends any[]> {
  onBefore?: (params: TParams) =>
    | ({
        stopNow?: boolean;
        returnNow?: boolean;
      } & Partial<FetchState<TData, TParams>>)
    | void;

  onRequest?: (
    service: Service<TData, TParams>,
    params: TParams
  ) => {
    servicePromise?: Promise<TData>;
  };

  onSuccess?: (data: TData, params: TParams) => void;
  onError?: (e: Error, params: TParams) => void;
  onFinally?: (params: TParams, data?: TData, e?: Error) => void;
  onCancel?: () => void;
  onMutate?: (data: TData) => void;
}

// for useRequestImplement
export interface UseRequestOptions<TData, TParams extends any[]> {
  // 如果设置了 options.manual = true ，
  // 则 useRequest 不会默认执行，需要通过 run 来触发执行。
  // const { loading, run } = useRequest(imitateApi, { manual: true });
  manual?: MaybeRef<boolean>;
  initialData?: TData;

  /**
   *
   *onBefore请求之前触发
onSuccess请求成功触发
onError请求失败触发
onFinally请求完成触发
   */
  onBefore?: (params: TParams) => void;
  onSuccess?: (data: TData, params: TParams) => void;
  onError?: (e: Error, params: TParams) => void;
  // formatResult?: (res: any) => TData;
  onFinally?: (params: TParams, data?: TData, e?: Error) => void;
  // useRequest 返回的 params 会记录当次调用 service 的参数数组。
  // 比如你触发了run(1, 2, 3),则 params 等于 [1, 2, 3]
  // 如果我们设置了 options.manual = false ，则首次调用 service 的参数可以通过 options.defaultParams 来设置。
  defaultParams?: TParams;

  // refreshDeps
  // 可以是计算类型 ref
  // options.refreshDeps 可以是一个方法，但必须是一个关于响应型数据 Reactive、Ref、Computed 的 effect 函数
  // const { data, loading } = useRequest(() => imitateApi(select.value), { refreshDeps: [select] });
  refreshDeps?: WatchSource<any>[];
  // refreshDepsAction 可以是方法，改变值后 触发的方法 而不执行原有service
  refreshDepsAction?: () => void;

  // const { loading, data } = useRequest(imitateApi, { loadingDelay: 300 });
  // loading delay 最少多长时间后 loading 才true
  loadingDelay?: number;

  // polling 轮询
  // const { data, run, cancel } = useRequest(imitateApi, { pollingInterval: 3000 });
  pollingInterval?: number;
  // 不可见的时候 切换浏览器表情 是否还轮询
  pollingWhenHidden?: boolean;
  // 通过 options.pollingErrorRetryCount 轮询错误重试次数。
  pollingErrorRetryCount?: number;

  // refresh on window focus 浏览器窗口重新获取焦点的时候 刷新请求
  refreshOnWindowFocus?: boolean;
  // 相当于聚焦请求的防抖 默认5000ms
  focusTimespan?: number;

  // debounce
  debounceWait?: number;
  // leading: true + trailing: true：第一次和最后一次均可能执行。
  // leading: true + trailing: false：仅执行第一次。
  // leading: false + trailing: true（默认）：仅执行最后一次。
  debounceLeading?: boolean;
  // 末尾执行
  debounceTrailing?: boolean;
  // 隔多久执行一次 默认不执行
  debounceMaxWait?: number;

  // throttle
  throttleWait?: number;
  throttleLeading?: boolean;
  throttleTrailing?: boolean;

  // cache
  // 缓存的key值
  cacheKey?: string;
  // 默认5分钟  毫秒计算
  cacheTime?: number;
  // 在这个时间内，不会重新发起请求。
  staleTime?: number;
  // const { loading, data } = useRequest(getArticle, {
  //   cacheKey,
  //   setCache: (data) => localStorage.setItem(cacheKey, JSON.stringify(data)),
  //   getCache: () => JSON.parse(localStorage.getItem(cacheKey) || '{}'),
  // });
  setCache?: (data: CachedData<TData, TParams>) => void;
  getCache?: (params: TParams) => CachedData<TData, TParams> | undefined;

  // retry
  // 通过设置 options.retryCount ，指定错误重试次数，则 useRequest 在失败后会进行重试。
  // retryCount?: number; // -1, 无限次重试
  retryCount?: number;
  // 重试时间间隔
  retryInterval?: number;

  // ready
  // 每次 ready 从 false 变为 true 时，都会重新发起请求。
  // const ready = ref(false);
  // const { data, loading } = useRequest(imitateApi, { ready });
  ready?: MaybeRef<boolean>;

  // [key: string]: any;
}

export interface UseRequestPlugin<TData, TParams extends any[]> {
  // eslint-disable-next-line prettier/prettier
  (fetchInstance: Fetch<TData, TParams>, options: UseRequestOptions<TData, TParams>): PluginReturn<
    TData,
    TParams
  >;
  onInit?: (options: UseRequestOptions<TData, TParams>) => Partial<FetchState<TData, TParams>>;
}

// for index
// export type OptionsWithoutFormat<TData, TParams extends any[]> = Omit<Options<TData, TParams>, 'formatResult'>;

// export interface OptionsWithFormat<TData, TParams extends any[], TFormated, TTFormated extends TFormated = any> extends Omit<Options<TTFormated, TParams>, 'formatResult'> {
//   formatResult: (res: TData) => TFormated;
// };

export interface UseRequestResult<TData, TParams extends any[]> {
  loading: Ref<boolean>;
  data: Ref<TData>;
  error: Ref<Error>;
  params: Ref<TParams | []>;
  // useRequest 提供了 cancel 函数，用于忽略当前 promise 返回的数据和错误
  cancel: Fetch<TData, TParams>["cancel"];
  // useRequest 提供了 refresh 和 refreshAsync 方法，使我们可以使用上一次的参数，重新发起请求。
  refresh: Fetch<TData, TParams>["refresh"];
  refreshAsync: Fetch<TData, TParams>["refreshAsync"];
  run: Fetch<TData, TParams>["run"];
  runAsync: Fetch<TData, TParams>["runAsync"];
  mutate: Fetch<TData, TParams>["mutate"];
}

export type UseRequestTimeout = ReturnType<typeof setTimeout>;
